Source code for hysop.backend.hardware.pci

# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import re, itertools as it

from hysop.tools.henum import EnumFactory
from hysop.tools.string_utils import prepend
from hysop.backend.hardware.hwinfo import TopologyObject, bytes2str

OperatingSystemDeviceType = EnumFactory.create(
    "OperatingSystemDeviceType",
    {
        "BLOCK_DEVICE": 0x0,  # Operating system block device.
        # For instance "sda" on Linux.
        "GPU_DEVICE": 0x1,  # Operating system GPU device.
        # For instance ":0.0" for a GL display, "card0" for a Linux DRM dev.
        "NETWORK_DEVICE": 0x2,  # Operating system network device.
        # For instance the "eth0" interface on Linux.
        "OPENFABRICS_DEVICE": 0x3,  # Operating system openfabrics device.
        # For instance the "mlx4_0" InfiniBand HCA device on Linux.
        "DMA_DEVICE": 0x4,  # Operating system dma engine device.
        # For instance the "dma0chan0" DMA channel on Linux.
        "COPROCESSOR_DEVICE": 0x5,  # Operating system co-processor device.
        # For instance "mic0" for a Xeon Phi (MIC) on Linux,
        # "opencl0d0" for a OpenCL device, "cuda0" for a CUDA device.
    },
)
"""
Type of an Operating System device (pci device function).
See hwloc documentation on type 'hwloc_obj_osdev_type_e'.
"""


[docs] class OperatingSystemDevice(TopologyObject): def __init__(self, parent, device): super().__init__(parent, device) def _parsed_type(self): return "OSDev" def _parse_object(self, it): raise ValueError(f"Unknown object type {_type}.")
[docs] def name(self): return self.attribute("name")
[docs] def osdev_type(self): return self.attribute("osdev_type", 0, int)
# type 0 specific
[docs] def type(self): assert self.osdev_type() == 0 if "type" in self._attributes: return self.attribute("type") else: return ""
[docs] def model(self): assert self.osdev_type() == 0 if "model" in self._attributes: return self.attribute("model") else: return ""
[docs] def serial_number(self): assert self.osdev_type() == 0 return self.attribute("serial_number")
[docs] def linux_device_id(self): assert self.osdev_type() == 0 return self.attribute("linux_device_id")
# type 2 specific
[docs] def address(self): assert self.osdev_type() == 2 return self.attribute("address")
# type 6 specific
[docs] def backend(self): assert self.osdev_type() == 5 return self.attribute("backend")
[docs] def backend_info(self): assert self.osdev_type() == 5 if self.backend() == "CUDA": self.print_attributes() multi_processors = self.attribute("cuda_multi_processors", 0, int) cores_per_mp = self.attribute("cuda_cores_per_mp", 0, int) global_mem = (self.attribute("cuda_global_memory_size", (0,)),) shared_mem_per_mp = ( self.attribute("cuda_shared_memory_size_per_mp", (0,)), ) l2_cache_size = (self.attribute("cudal2_cache_size", (0,)),) return { "multi_processors": multi_processors, "cores_per_mp": cores_per_mp, "cores": cores_per_mp * multi_processors, "global_memory_size": ", ".join( tuple( bytes2str(int(mem) * 1024 * 1024 // 1000) for mem in global_mem ) ), "shared_memory_size_per_mp": ", ".join( tuple(bytes2str(int(mem) * 1000) for mem in shared_mem_per_mp) ), "l2_cache_size": ", ".join( tuple(bytes2str(int(mem) * 1000) for mem in l2_cache_size) ), } else: return None
def __str__(self): _type = self.osdev_type() osdev_type = OperatingSystemDeviceType[_type] if osdev_type == OperatingSystemDeviceType.BLOCK_DEVICE: return f"block_device: {self.type()} {self.model()} /dev/{self.name()}" elif osdev_type == OperatingSystemDeviceType.GPU_DEVICE: return f"gpu_device: {self.name()}" elif osdev_type == OperatingSystemDeviceType.NETWORK_DEVICE: return f"network_device: {self.name()} {self.address()}" elif osdev_type == OperatingSystemDeviceType.OPENFABRICS_DEVICE: return "openfabrics_device: {} node_guid={}".format( self.name(), self.attribute("node_guid") ) elif osdev_type == OperatingSystemDeviceType.DMA_DEVICE: return "dma_device: PRINTING_NOT_IMPLEMENTED" elif osdev_type == OperatingSystemDeviceType.COPROCESSOR_DEVICE: header = f"coprocessor /dev/{self.name()} on backend {self.backend()}" backend_info = self.backend_info() if backend_info: content = "" for k, v in backend_info.items(): content += f"\n*{k}: {v}" return header + self.indent(content) else: return "coprocessor_device: PRINTING_NOT_IMPLEMENTED" else: self.print_attributes() raise ValueError( "Unimplemented osdev printing for type {} ({}).".format( osdev_type, _type ) ) return content
[docs] class PciDevice(TopologyObject): def __init__(self, parent, device): self._os_devices = [] super().__init__(parent, device)
[docs] def leaf_pci_devices(self): return [self]
def _post_init(self): pci_type = self.pci_type() regexp = r"([a-f0-9]{4})\s+\[([a-f0-9]{4}):([a-f0-9]{4})\]\s+" regexp += r"\[([a-f0-9]{4}):([a-f0-9]{4})\]\s+([a-f0-9]{2})" regexp = re.compile(regexp) match = re.match(regexp, pci_type) if not match: msg = "Could not match pci device type '{}'." msg = msg.format(pci_type) raise ValueError(msg) pci_device_class_id = match.group(1) vendor_id = match.group(2) device_id = match.group(3) subvendor_id = match.group(4) subdevice_id = match.group(5) revision = match.group(6) pci_device_class = self.pciids.find_device_class_by_id(pci_device_class_id) vendor = self.pciids.find_vendor(vendor_id) if not vendor: vendor = vendor_id device = None else: device = vendor.find_device(device_id, subdevice_id) if not device: device = f"[{vendor_id}:{device_id}]" subvendor = self.pciids.find_vendor(subvendor_id) if not subvendor: subvendor = subvendor_id subdevice = None else: subdevice = subvendor.find_device(subdevice_id) if not subdevice: subdevice = f"[{subvendor_id}:{subdevice_id}]" self._attributes["pci_device_class_sid"] = pci_device_class_id self._attributes["pci_system_vendor_sid"] = vendor_id self._attributes["pci_system_device_sid"] = device_id self._attributes["pci_subsystem_vendor_sid"] = subvendor_id self._attributes["pci_subsystem_device_sid"] = subdevice_id self._attributes["pci_device_revision_string"] = revision self._attributes["pci_device_class_id"] = int(pci_device_class_id, 16) self._attributes["pci_system_vendor_id"] = int(vendor_id, 16) self._attributes["pci_system_device_id"] = int(device_id, 16) self._attributes["pci_subsystem_vendor_id"] = int(subvendor_id, 16) self._attributes["pci_subsystem_device_id"] = int(subdevice_id, 16) self._attributes["pci_device_revision_value"] = int(revision, 16) self._attributes["pci_device_class"] = pci_device_class self._attributes["pci_system_vendor"] = vendor self._attributes["pci_system_device"] = device self._attributes["pci_subsystem_vendor"] = subvendor self._attributes["pci_subsystem_device"] = subdevice
[docs] def pci_busid(self): return self.attribute("pci_busid")
[docs] def pci_type(self): return self.attribute("pci_type")
[docs] def vendor(self): return self.pci_system_vendor()
[docs] def device(self): return self.pci_system_device()
[docs] def vendor_id(self): return self.pci_system_vendor_id()
[docs] def device_id(self): return self.pci_system_device_id()
[docs] def subdevices(self): return self.operating_system_devices()
[docs] def subdevices_count(self): return self.operating_system_devices_count()
[docs] def pci_device_class_id(self): return self.attribute("pci_device_class_id")
[docs] def pci_device_class_sid(self): return self.attribute("pci_device_class_sid")
[docs] def pci_device_class(self): return self.attribute("pci_device_class")
[docs] def pci_device_revision_val(self): return self.attribute("pci_device_revision_value")
[docs] def pci_device_revision_str(self): return self.attribute("pci_device_revision_string")
[docs] def pci_system_vendor_id(self): return self.attribute("pci_system_vendor_id")
[docs] def pci_system_vendor_sid(self): return self.attribute("pci_system_vendor_sid")
[docs] def pci_system_vendor(self): return self.attribute("pci_system_vendor")
[docs] def pci_system_device_id(self): return self.attribute("pci_system_device_id")
[docs] def pci_system_device_sid(self): return self.attribute("pci_system_device_sid")
[docs] def pci_system_device(self): return self.attribute("pci_system_device")
[docs] def pci_subsystem_vendor_id(self): return self.attribute("pci_subsystem_vendor_id")
[docs] def pci_subsystem_vendor_sid(self): return self.attribute("pci_subsystem_vendor_sid")
[docs] def pci_subsystem_vendor(self): return self.attribute("pci_subsystem_vendor")
[docs] def pci_subsystem_device_id(self): return self.attribute("pci_subsystem_device_id")
[docs] def pci_subsystem_device_sid(self): return self.attribute("pci_subsystem_device_sid")
[docs] def pci_subsystem_device(self): return self.attribute("pci_subsystem_device")
[docs] def operating_system_devices_count(self): return len(self._os_devices)
[docs] def operating_system_devices(self): return self._os_devices
[docs] def to_string(self, expand_pci_tree=True, **kargs): if expand_pci_tree: header = f"{self.pci_busid()} {self.pci_device_class()}" content = f"\nvendor: {self.pci_system_vendor()}" content += f"\ndevice: {self.pci_system_device()}" if self.pci_system_vendor_id() != self.pci_subsystem_vendor_id(): content += f"\nsubvendor: {self.pci_subsystem_vendor()}" content += f"\nsubdevice: {self.pci_subsystem_device()}" content += f"\nrevision: 0x{self.pci_device_revision_str()}" if self.subdevices_count() > 0: subcontent = "" for osdev in self.subdevices(): subcontent += f"\n> {osdev}" content += prepend(subcontent, 2 * " ") content += "\n" return header + prepend(content, 5 * " ") else: return "{} {} ({})".format( self.pci_busid(), self.device(), self.pci_device_class().name )
def __str__(self): return self.to_string() def _parsed_type(self): return "PCIDev" def _parse_object(self, it): _type = it.attrib["type"] if _type == "OSDev": obj = OperatingSystemDevice(self, it) self._os_devices.append(obj) else: raise ValueError(f"Unknown object type {_type}.")
[docs] class PciBridge(TopologyObject): def __init__(self, parent, bridge): self._pci_devices = [] super().__init__(parent, bridge)
[docs] def pci_devices(self, split=False): devs = tuple(sorted(self._pci_devices, key=lambda x: x.os_index())) if split: devices = [dev for dev in devs if isinstance(dev, PciBridge)] devices += [dev for dev in devs if isinstance(dev, PciDevice)] return devices else: return devs
[docs] def pci_devices_count(self): return len(self._pci_devices)
[docs] def leaf_pci_devices(self): return it.chain.from_iterable([x.leaf_pci_devices() for x in self._pci_devices])
[docs] def bridge_pci(self): return self.attribute("bridge_pci")
[docs] def bridge_type(self): return self.attribute("bridge_type")
[docs] def bridge_depth(self): return self.attribute("depth", 0, int)
def __str__(self): return self.to_string()
[docs] def to_string(self, expand_pci_tree=True, is_last=False): header = f"Bridge {self.bridge_pci()}" content = "" devices = self.pci_devices(split=True) is_root = self.bridge_depth() == 0 if is_root: prefix = "x-" else: prefix = "" if (not is_root) and (is_last): extra_pad = 3 else: extra_pad = 0 if expand_pci_tree and self.pci_devices_count() == 1: extra_bar = "|\n" else: extra_bar = "" for dev_id, pci_device in enumerate(devices[:-1]): pci_device = pci_device.to_string(expand_pci_tree) pci_device = pci_device.split("\n") pci_device[0] = "|--" + pci_device[0] for i in range(1, len(pci_device)): pci_device[i] = "| " + pci_device[i] if (dev_id == 0) and expand_pci_tree: pci_device = ["|"] + pci_device pci_device = "\n".join(pci_device) branch = self.indent(pci_device, len(prefix)) content += "\n" + branch pci_device = devices[-1].to_string(expand_pci_tree, is_last=True) branch = self.indent(f"{extra_bar}|__{pci_device}", extra_pad + len(prefix)) content += "\n" + branch return prefix + header + content
def _parsed_type(self): return "Bridge" def _parse_object(self, it): _type = it.attrib["type"] if _type == "PCIDev": obj = PciDevice(self, it) self._pci_devices.append(obj) elif _type == "Bridge": obj = PciBridge(self, it) self._pci_devices.append(obj) else: raise ValueError(f"Unknown object type {_type}.")